电子数显控件,隔壁产品都馋哭了
The following article is from 也路子 Author 彭也
阅读完本文约需6分钟。
废不说,有图
模拟电子手表的时钟数显,适合用在时钟、数字电子风格显示等场景。
可以注意到,本控件还原了电子手表上数字显示的残影效果,在显示某个具体数字时,仍然可以隐约看见数字“8”的形状。
将这个控件用在大屏全面屏手机上,作为一个工作时钟也是个不错的选择。
一、设计思路
模拟现实生活中的上古时代电子手表进行设计还原。为更加贴近现实中的电子手表,也提供了像素栅格的绘制
是不是更有那味儿了?
二、实现方式
2.1 UI拆解
2.1.1 形状分析
可以观察到,控件由同一个基础形状通过不同位置的摆放组合而成
2.1.2 数字分类
所有的数字都由数字“8”的形状组成,不同部位涂抹不同颜色而呈现不同数字
2.2 UI绘制
2.2.1 绘制基本形状
定义全局的mPath变量用于绘制基本形状,封装一个绘制基本形状的方法
/**
* 绘制骨架单元
*
* @param w 宽
* @param h 高
*/
private void drawUnit(Canvas canvas, int w, int h) {
if (mPath == null) {
mPath = new Path();
// 间隔,相当于padding
int d = 5;
mPath.moveTo(d, h / 2);
mPath.lineTo(h / 2 + d, 0);
mPath.lineTo(w - h / 2 - d, 0);
mPath.lineTo(w - d, h / 2);
mPath.lineTo(w - h / 2 - d, h);
mPath.lineTo(h / 2 + d, h);
mPath.lineTo(d, h / 2);
mPath.close();
}
canvas.drawPath(mPath, unitPaint);
//todo:绘制网格
}
2.2.2 绘制数字“8”
为表现出数显残影效果,所有的数字由数字“8”的形状组成,涂抹不同位置的颜色显示不同的数字。绘制完基本形状后,对基本形状进行旋转、平移等操作即可组成数字“8”形状。
/**
* 绘制数字
*
* @param canvas
* @param num 数字
*/
private void drawNum(Canvas canvas, int num) {
//绘制第1个
unitPaint.setColor(NumColors.getTargetNumColors(num).colors[0]);
canvas.translate(uintH / 2, 0);
drawUnit(canvas, uintW, uintH);
//绘制第2个
unitPaint.setColor(NumColors.getTargetNumColors(num).colors[1]);
canvas.translate(uintW + uintH / 2, 0);
canvas.rotate(90);
canvas.translate(uintH / 2, 0);
drawUnit(canvas, uintW, uintH);
//绘制第3个
unitPaint.setColor(NumColors.getTargetNumColors(num).colors[2]);
canvas.translate(uintW, 0);
drawUnit(canvas, uintW, uintH);
//绘制第4个
unitPaint.setColor(NumColors.getTargetNumColors(num).colors[3]);
canvas.translate(uintW + uintH / 2, 0);
canvas.rotate(90);
canvas.translate(uintH / 2, 0);
drawUnit(canvas, uintW, uintH);
//绘制第5个
unitPaint.setColor(NumColors.getTargetNumColors(num).colors[4]);
canvas.translate(uintW + uintH / 2, 0);
canvas.rotate(90);
canvas.translate(uintH / 2, 0);
drawUnit(canvas, uintW, uintH);
//绘制第6个
unitPaint.setColor(NumColors.getTargetNumColors(num).colors[5]);
canvas.translate(uintW, 0);
drawUnit(canvas, uintW, uintH);
//绘制第7个
unitPaint.setColor(NumColors.getTargetNumColors(num).colors[6]);
canvas.rotate(90);
canvas.translate(uintH / 2, -uintH / 2);
drawUnit(canvas, uintW, uintH);
}
2.2.3 数字枚举
有了数字“8”形状的骨架,定义不同的数字枚举即可实现不同数字的绘制。同时定义好残影颜色和数显颜色,方便日后定制扩展。
/**
* 默认骨架颜色
*/
@ColorInt
public static int uintBgColor = Color.parseColor("#e8e8e8");
/**
* 选中的骨架颜色
*/
@ColorInt
public static int uintSelectedColor = Color.DKGRAY;
/**
* 每个数字对应的颜色数组枚举
*/
enum NumColors {
NUM_0(new int[]{uintSelectedColor, uintSelectedColor, uintSelectedColor, uintSelectedColor, uintSelectedColor, uintSelectedColor, uintBgColor}),
NUM_1(new int[]{uintBgColor, uintSelectedColor, uintSelectedColor, uintBgColor, uintBgColor, uintBgColor, uintBgColor}),
NUM_2(new int[]{uintSelectedColor, uintSelectedColor, uintBgColor, uintSelectedColor, uintSelectedColor, uintBgColor, uintSelectedColor}),
NUM_3(new int[]{uintSelectedColor, uintSelectedColor, uintSelectedColor, uintSelectedColor, uintBgColor, uintBgColor, uintSelectedColor}),
NUM_4(new int[]{uintBgColor, uintSelectedColor, uintSelectedColor, uintBgColor, uintBgColor, uintSelectedColor, uintSelectedColor}),
NUM_5(new int[]{uintSelectedColor, uintBgColor, uintSelectedColor, uintSelectedColor, uintBgColor, uintSelectedColor, uintSelectedColor}),
NUM_6(new int[]{uintSelectedColor, uintBgColor, uintSelectedColor, uintSelectedColor, uintSelectedColor, uintSelectedColor, uintSelectedColor}),
NUM_7(new int[]{uintSelectedColor, uintSelectedColor, uintSelectedColor, uintBgColor, uintBgColor, uintBgColor, uintBgColor}),
NUM_8(new int[]{uintSelectedColor, uintSelectedColor, uintSelectedColor, uintSelectedColor, uintSelectedColor, uintSelectedColor, uintSelectedColor}),
NUM_9(new int[]{uintSelectedColor, uintSelectedColor, uintSelectedColor, uintSelectedColor, uintBgColor, uintSelectedColor, uintSelectedColor});
/**
* 数字对应的颜色数组
*/
int colors[];
NumColors(int[] colors) {
this.colors = colors;
}
public static NumColors getTargetNumColors(int num) {
switch (num) {
case 0:
return NUM_0;
case 1:
return NUM_1;
case 2:
return NUM_2;
case 3:
return NUM_3;
case 4:
return NUM_4;
case 5:
return NUM_5;
case 6:
return NUM_6;
case 7:
return NUM_7;
case 8:
return NUM_8;
case 9:
return NUM_9;
default:
return NUM_0;
}
}
}
2.3 栅格绘制
栅格绘制使用到了Paint绘制时的叠加方式,思路是这样的,先绘制基本形状,再在裁剪成基本形状的画布上绘制栅格,最后排除重叠的栅格绘制内容
2.3.1 绘制的叠加方式简介
通过Paint.setXfermode进行设置,参数通过PorterDuff.Mode枚举进行选取。
2.3.2 代码绘制
确定好叠加模式后,裁剪出基本形状的画布,继而绘制栅格。需要注意的是调用restore方法,方便其它基本形状位置的摆放绘制。
//绘制网格
if (isDrawGrid) {
canvas.save();
canvas.clipPath(mPath);
canvas.drawPath(mPath, unitPaint);
unitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
canvas.drawPath(getGridPath(w, h), unitPaint);
unitPaint.setXfermode(null);
canvas.restore();
} else {
canvas.drawPath(mPath, unitPaint);
}
最后的效果
三、后记
所有的UI交互控件都应该是基于现实的抽象与改进,集可用性、美观性、便捷性于一体。
从控件设计和实现一窥相关行业方向。控件设计可能会经历以下三个阶段:
第一阶段:对现实生活中的工具进行抽象,演化成不同的UI提供给用户;
第二阶段:在抽象的基础上进行派生,新增更适合手机屏幕的平面交互方式;
第三阶段:回归现实,所有的控件成为现实生活中的一部分,它可能在你家的沙发上,也可能在你家的浴室里,完全融入现实生活而不显突兀。
控件地址在gitee
点“阅读原文”获取
---END---
更文不易,点个“在看”支持一下👇